#include "be_model_system.h"
#include "utils/texturestore.h"

void Model::upload()
{
	if(!m_isUploaded)
	{
		assert(m_arrayBuffer==0);
		m_arrayBuffer.reset(new BeArrayBuffer);
		assert(m_arrayBuffer);

		m_arrayBuffer->data((m_numberVertices*sizeof(btScalar))+((m_numberNormals+m_numberTexCoor)*sizeof(GLfloat)), NULL, GL_STATIC_DRAW_ARB);
		m_arrayBuffer->subData(0, m_numberVertices*sizeof(btScalar), vertices.get());
		m_arrayBuffer->subData(m_numberVertices*sizeof(btScalar), m_numberNormals*sizeof(GLfloat), normals.get());
		m_arrayBuffer->subData((m_numberVertices*sizeof(btScalar))+(m_numberNormals*sizeof(GLfloat)), m_numberTexCoor*sizeof(GLfloat), texcoor.get());

		assert(m_elementArrayBuffer==0);
		m_elementArrayBuffer.reset(new BeElementArrayBuffer);
		assert(m_elementArrayBuffer);
		m_elementArrayBuffer->data(m_numberVIndices*sizeof(GLint), indices.get(), GL_STATIC_DRAW);

		m_vertexArray.reset(new BeVertexArray);
		m_vertexArray->setElementArrayBuffer(m_elementArrayBuffer.get());
		m_vertexArray->vertexPointer( *m_arrayBuffer, 3, GL_FLOAT, 0, 0 );
		m_vertexArray->normalPointer( *m_arrayBuffer, GL_FLOAT, 0, m_numberVertices*sizeof(btScalar) );
		m_vertexArray->texCoordPointer( *m_arrayBuffer, 2, GL_FLOAT, 0, ( (m_numberVertices*sizeof(btScalar)) + (m_numberNormals*sizeof(GLfloat)) ) );
		m_vertexArray->enableClientState(GL_VERTEX_ARRAY);
		m_vertexArray->enableClientState(GL_NORMAL_ARRAY);
		m_vertexArray->enableClientState(GL_TEXTURE_COORD_ARRAY);

		// upload textures
		for ( matit = matlist.begin(); matit != matlist.end(); matit++ )
		{
			if ( matit->second.m_imagePath.size() )
			{
				matlist[ matit->first ].m_imageTexture2D = Texturestore::Instance()->load(matit->second.m_imagePath);
			}
		}
	
		m_isUploaded = true;
	}
}

void Model::draw( btScalar* drawposition )
{

	m_graphicsSystem.matrixPush(GL_MODELVIEW);
	m_graphicsSystem.matrixMult(GL_MODELVIEW, drawposition);
	draw();
	m_graphicsSystem.matrixPop(GL_MODELVIEW);

	//glEnable(GL_CULL_FACE);
	//glCullFace(GL_BACK);
}

void Model::draw()
{
	m_graphicsSystem.enable(GL_TEXTURE_2D);

	m_graphicsSystem.bindVertexArray(m_vertexArray.get());

	if ( facematerials.size() > 0 )
	{
		for ( unsigned int i=0; i < facematerials.size(); i++ )
		{
			const Facematerial& facematerial = facematerials[i];
			const Material& material = matlist[facematerial.material];
			m_graphicsSystem.bindTexture2D(material.m_imageTexture2D.get());
			m_graphicsSystem.applyMaterial(GL_FRONT_AND_BACK, &material.m_material);
			glDrawElements(GL_TRIANGLES, facematerial.faceindex_number, GL_UNSIGNED_INT, (char *)NULL + ((facematerial.faceindex_first)*sizeof(GLint)) );
		}
	}
	else
	{
		m_graphicsSystem.bindTexture2D(0);
		m_graphicsSystem.applyMaterial(GL_FRONT_AND_BACK, 0);
		glDrawElements(GL_TRIANGLES, m_numberVIndices, GL_UNSIGNED_INT, NULL);
	}

	m_graphicsSystem.bindVertexArray(0);
	m_graphicsSystem.bindTexture2D(0);
	m_graphicsSystem.disable(GL_TEXTURE_2D);
}

BeModelSystem* BeModelSystem::Instance() 
{
	static BeModelSystem t;
	return &t;
}

BeModelSystem::BeModelSystem()
{
}

boost::shared_ptr<Model> BeModelSystem::load(const std::string& filename, const btVector3& scale)
{
	// check if models exists
		for ( unsigned int i=0; i < models.size(); i++ )
			if ( models[i]->getFilename() == filename )
				return models[i];
	
	BeFile objBeFile;
	if ( BeFilesystem::Instance()->load(objBeFile, filename) )
	{

	// model exists
		BeObjLoader obj( objBeFile );

		boost::shared_ptr<Model> m(new Model( filename ));

		// FIXME don't allocate if 0
		m->setNumberVertices( obj.getNumberVIndices()*3 );
		m->vertices.reset(new btScalar[m->getNumberVertices()]);

		m->setNumberNormals( obj.getNumberVIndices()*3 );
		m->normals.reset(new float[m->getNumberNormals()]);

		m->setNumberTexCoor( obj.getNumberVIndices()*2 );
		m->texcoor.reset(new float[m->getNumberTexCoor()]);

		m->setNumberVIndices( obj.getNumberVIndices() );
		m->indices.reset(new int[m->getNumberVIndices()]);

	// 	cerr << "model: " << m->name << endl;
	// 	cerr << "number of triangles: " << (m->m_numberVIndices/3) << endl;

		// copy materials
		m->matlist = obj.matlist;
		// copy face materials
		m->facematerials = obj.facematerials;

		// copy geometry
	// 	for ( unsigned int i=0; i < obj.m_numberVIndices; i++ )
	// 	{
	// 		unsigned int index = obj.gVIndices[i];
	// 		m->indices[i] = index;
	// 		
	// 		m->vertices[(index*3)]		= obj.gVertices[(index*3)] * scale; // +1335
	// 		m->vertices[(index*3)+1]	= obj.gVertices[(index*3)+1] * scale; // -4653.10f
	// 		m->vertices[(index*3)+2]	= obj.gVertices[(index*3)+2] * scale; // -4139
	// 		
	// 		unsigned int tindex = obj.gTIndices[i];
	// 		m->texcoor[(index*2)]	= obj.gTexCoor[(tindex*2)];
	// 		m->texcoor[(index*2)+1]	= obj.gTexCoor[(tindex*2)+1];
	// 
	// 		unsigned int nindex = obj.gNIndices[i];
	// 		m->normals[(index*3)]	= obj.gNormals[(nindex*3)];
	// 		m->normals[(index*3)+1]	= obj.gNormals[(nindex*3)+1];
	// 		m->normals[(index*3)+2]	= obj.gNormals[(nindex*3)+2];
	// 	}

		for ( unsigned int i=0; i < obj.getNumberVIndices(); i++ )
		{
			m->indices[i] = i;
			
			int index = (obj.gVIndices[i] - 1) * 3;
			
			m->vertices[(i*3)]	= obj.gVertices[index] * scale.getX(); // +1335
			m->vertices[(i*3)+1]	= obj.gVertices[index+1] * scale.getY(); // -4653.10f
			m->vertices[(i*3)+2]	= obj.gVertices[index+2] * scale.getZ(); // -4139
			
			if ( obj.gTIndices[i] > 0 )
			{
				int tindex = (obj.gTIndices[i] - 1) * 2;

				m->texcoor[(i*2)]	= obj.gTexCoor[(tindex)];
				m->texcoor[(i*2)+1]	= obj.gTexCoor[(tindex)+1];
			}
			else
			{
				cerr << "zero" << endl;
				m->texcoor[(i*2)]	= 0;
				m->texcoor[(i*2)+1]	= 0;
			}

			if ( obj.gNIndices[i] > 0 )
			{
				int nindex = (obj.gNIndices[i] - 1) * 3;

				m->normals[(i*3)]	= obj.gNormals[(nindex)];
				m->normals[(i*3)+1]	= obj.gNormals[(nindex)+1];
				m->normals[(i*3)+2]	= obj.gNormals[(nindex)+2];
			}
			else
			{
				m->normals[(i*3)]	= 0;
				m->normals[(i*3)+1]	= 0;
				m->normals[(i*3)+2]	= 0;
			}
		}

		models.push_back(m);

		return m;
	}

	// return 0
	return boost::shared_ptr<Model>();
}

void BeModelSystem::emptyStore()
{
	models.clear();
}

BeModelSystem::~BeModelSystem()
{
	emptyStore();
}


